package com.flyiu.data.consistency.bean;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.flyiu.data.consistency.annotations.DistributedTransaction;
import com.flyiu.data.consistency.annotations.DistributedTransactionPartner;
import com.flyiu.data.consistency.registry.PartnerMeteDateRegistry;

/**
 * 
 * @ClassName: PartnerMetaDataProcessor
 * @Description: TODO
 * @author: chenjh
 * @date: 2017年10月10日 下午3:05:43
 */
@Component
public class PartnerMetaDataProcessor implements BeanPostProcessor {

	private static Logger LOG = LoggerFactory.getLogger(PartnerMetaDataProcessor.class);

	/**
	 * 
	 * @ClassName: PartnerMetaData
	 * @Description: TODO
	 * @author: chenjh
	 * @date: 2017年10月10日 下午3:14:25
	 */
	public static class PartnerMetaData {
		private Method commitMethod;
		private Method rollbackMethod;

		private Object bean;

		private Type[] parameterTypes;

		public Method getCommitMethod() {
			return commitMethod;
		}

		public void setCommitMethod(Method commitMethod) {
			this.commitMethod = commitMethod;
		}

		public Method getRollbackMethod() {
			return rollbackMethod;
		}

		public void setRollbackMethod(Method rollbackMethod) {
			this.rollbackMethod = rollbackMethod;
		}

		public Object getBean() {
			return bean;
		}

		public void setBean(Object bean) {
			this.bean = bean;
		}

		public Type[] getParameterTypes() {
			return parameterTypes;
		}

		public void setParameterTypes(Type[] parameterTypes) {
			this.parameterTypes = parameterTypes;
		}

	}

	/**
	 * 
	 * registerPartnerMeteData
	 * 
	 * @Description: TODO(这里用一句话描述这个方法的作用)
	 * @param distributedTransaction
	 * @param bean
	 *            设定文件
	 */
	private static void registerPartnerMeteData(DistributedTransactionPartner distributedTransaction, Object bean) {

		Function<? super String, ? extends PartnerMetaData> mappingFunction = key -> {
			Method[] methods = bean.getClass().getMethods();
			PartnerMetaData metaData = new PartnerMetaData();

			metaData.bean = bean;
			for (Method method : methods) {
				if (method.getName().equals(distributedTransaction.commitMethod())) {
					metaData.commitMethod = method;
					metaData.parameterTypes = method.getGenericParameterTypes();
				}
				if (method.getName().equals(distributedTransaction.rollbackMethod())) {
					metaData.rollbackMethod = method;
					metaData.parameterTypes = method.getGenericParameterTypes();
				}
			}
			return metaData;
		};

		PartnerMeteDateRegistry.regist(distributedTransaction.name(),
				mappingFunction.apply(distributedTransaction.name()));

	}

	// public void onApplicationEvent(ContextRefreshedEvent event) {
	// if (event.getApplicationContext().getParent() == null) {
	// Map<String, Object> beans = event.getApplicationContext()
	// .getBeansWithAnnotation(DistributedTransactionPartner.class);
	// beans.entrySet().forEach(action->{
	// Method[] methods = action.getValue().getClass().getMethods();
	// for (Method method : methods) {
	// AnnotationUtils.findAnnotation(arg0, arg1)
	// if(method.getAnnotation(annotationClass))
	// }
	// });

	// }
	// }

	String keyIndexError = "keyIndex 与方法参数个数不符，请检查后重试，如不需要设置businessid，可将index设置为负数";

	/**
	 * 验证参数index
	 * 
	 * @param keyIndex
	 * @param method
	 */
	private void validIndex(int keyIndex, Method method) {
		Assert.isTrue(method.getParameterCount() - 1 >= keyIndex, keyIndexError);
	}

	/**
	 * 验证封装对象中是否包含对应属性
	 * 
	 * @param keyIndex
	 * @param keyName
	 * @param method
	 */
	private void validName(int keyIndex, String keyName, Method method) {
		if (StringUtils.isEmpty(keyName)) {
			return;
		}
		String name = "get" + keyName.substring(0, 1).toUpperCase() + keyName.substring(1);
		try {
			method.getParameterTypes()[keyIndex].getMethod(name);
		} catch (Exception e) {
			e.printStackTrace();
			throw new IllegalArgumentException("keyName 与参数实际设置不符，对象" + method.getParameterTypes()[keyIndex] + "中不存在"
					+ name + "() 方法，如非封装类型可以直接使用");
		}
	}

	/**
	 * 处理 @DistributedTransactionPartner
	 * 
	 * @param method
	 * @param bean
	 */
	private void processPartner(Method method, Object bean) {
		DistributedTransactionPartner partnerAnnotation = AnnotationUtils.findAnnotation(method,
				DistributedTransactionPartner.class);
		if (partnerAnnotation != null) {
			registerPartnerMeteData(partnerAnnotation, bean);
		}
	}

	/**
	 * 处理@DistributedTransaction
	 * 
	 * @param method
	 */
	private void processTransaction(Method method) {
		DistributedTransaction distributedAnnotation = AnnotationUtils.findAnnotation(method,
				DistributedTransaction.class);
		if (distributedAnnotation != null) {
			validIndex(distributedAnnotation.keyIndex(), method);
			validName(distributedAnnotation.keyIndex(), distributedAnnotation.keyName(), method);
		}
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		Method[] methods = bean.getClass().getMethods();
		for (Method method : methods) {
			processTransaction(method);
			processPartner(method, bean);
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		// TODO Auto-generated method stub
		return bean;
	}

}
